/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import { BaseCommand, pickedFaceData, pickedModelData } from '@amcax/base';
import { convertMatrix } from '@amcax/renderer';
import { PickInputRJSFFormat } from '@app-cad/common/components/command-form/constants';
import { PickableCommandForm } from '@app-cad/common/components/command-form/pickable';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { useTools } from '@app-cad/common/hooks/useTools';
import { getIdOfPickedObj } from '@app-cad/common/utils/render';
import { ctx2, previewer } from '@app-cad/module';
import { wsApi } from '@app-cad/server';
import { FeaturedCmdDialogParams } from '@app-cad/types';
import { RJSFSchema } from '@rjsf/utils';
import { useAsyncEffect } from 'ahooks';
import i18n from 'i18next';
import { useState } from 'react';
import { useDollyCamera } from '../common/hooks/useDollyCamera';
import { revertLastMatrix4 } from '../common/utils/RefDocUtils';

enum DatumPlane {
  XOY = 'XOY',
  YOZ = 'YOZ',
  XOZ = 'XOZ',
}

type FormData = {
  base: pickedModelData[];
  plane: pickedFaceData;
  datum: DatumPlane;
  keepSrc: boolean;
};

function Mirror({ title, onClose, props }: FeaturedCmdDialogParams) {
  const [formData, setFormData] = useState<FormData>({
    base: undefined,
    plane: null,
    datum: DatumPlane.XOY,
    keepSrc: true,
  });
  useDollyCamera();

  const [pickedNodeArr, setPickedNodeArr] = useState(null);
  const { picker } = useTools();

  useAsyncEffect(async () => {
    if (props == null) {
      setFormData({
        base: undefined,
        plane: null,
        datum: DatumPlane.XOY,
        keepSrc: true,
      });
      return;
    }

    const baseID = props.baseid;
    await initEditDependency([baseID]);
    const baseObj =
      baseID && (picker.generatePickObjByPickID(baseID) as pickedModelData);

    const { plane, datum, keepSrc } = props.params;
    const planeObj =
      plane &&
      typeof plane === 'string' &&
      (picker.generatePickObjByPickID(plane) as pickedFaceData);

    setFormData({
      base: [baseObj],
      plane: planeObj,
      datum: datum,
      keepSrc: keepSrc,
    });
  }, [props?.baseid, props?.shape]);

  useAsyncEffect(async () => {
    if (!isFormDataValid()) {
      pickedNodeArr?.forEach((pickedNode) => {
        pickedNode.destroy();
      });
      setPickedNodeArr(null);

      ctx2.viewer.requestRender();

      return;
    }
    await execute(true);
  }, [formData]);

  const isFormDataValid = () => {
    const { base, plane, keepSrc } = formData;

    return base?.length > 0 && plane != null && keepSrc != null;
  };

  const addDynamicParams = (
    params: Record<string, any>,
    dynamicParams: Record<string, any>,
  ) => {
    return { ...params, ...dynamicParams };
  };

  const execute = async (preview: boolean = false) => {
    const { base, plane, keepSrc } = formData;
    const baseID = base.map((item) => getIdOfPickedObj(item));
    const params = {
      keepSrc: keepSrc,
    };

    const dynamicParams = {
      plane: getIdOfPickedObj(plane),
    };
    const validParams = addDynamicParams(params, dynamicParams);
    const executionResult =
      props == null
        ? // ? await ctx2.commandCenters.assemblyCommands.mirror(
          //     baseID,
          //     validParams,
          //     preview,
          //   )
          await wsApi.createMirror(baseID, validParams, preview)
        : await wsApi.setParams((props as any).dxid, validParams, preview);

    if (preview) {
      pickedNodeArr?.forEach((pickedNode) => {
        pickedNode.destroy();
      });

      const pickedNodeGroupData = executionResult.map(({ dxid, matrix }) => {
        const pickedNode = base
          .find(({ idInfo }) => idInfo.o === dxid)
          .assisObj.cloneSelf();
        revertLastMatrix4(dxid, pickedNode);
        pickedNode.applyMatrix4(convertMatrix(matrix));
        ctx2.cadScene.assisGroup.add(pickedNode);
        return pickedNode;
      });
      setPickedNodeArr(pickedNodeGroupData);

      ctx2.viewer.requestRender();
    }
  };

  const checkAndFinish = async () => {
    if (!isFormDataValid()) {
      return;
    }

    await execute();

    exit();
  };

  const exit = () => {
    previewer.clear();
    onClose();
  };

  // =================
  const { initEditDependency } = useCommand('array-mirror', {
    onClose,
    finishDraw: checkAndFinish,
  });

  const schema: RJSFSchema = {
    properties: {
      plane: {
        type: 'string',
        title: '镜像面',
        format: PickInputRJSFFormat.PickPlane,
      },
      base: {
        type: 'array',
        title: '目标实体',
        items: {
          type: 'string',
          enum: [],
        },
        format: PickInputRJSFFormat.MultiPickProduct,
        uniqueItems: true,
      },
    },
  };

  return (
    <PickableCommandForm
      disabled={false}
      isFormDataValid={isFormDataValid()}
      onClose={onClose}
      title={title}
      schema={schema}
      formData={formData}
      onChange={(v) => {
        setFormData(v);
      }}
      extraUiSchema={{
        plane: {
          'ui:placeholder': '请选择镜像面',
          'ui:autofocus': true,
        },
        base: {
          'ui:placeholder': '请选择目标实体',
        },
        datum: {
          'ui:widget': 'radio',
        },
      }}
      submitText='完成'
      onSubmit={checkAndFinish}
    />
  );
}

export const cmdMirror: BaseCommand = {
  id: 'cmdMirror',
  label: 'command.mirror',
  icon: 'commands/feature/mirror',
  create: (label, onClose, props) => (
    <Mirror
      title={i18n.t(label)}
      onClose={onClose}
      props={props}
    />
  ),
};
